﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MetaCollection.Framework.Model;
using System.ComponentModel;
using MetaCollection.Framework.Configuration;
using MetaCollection.Framework.Repository;
using System.Diagnostics;
using System.Globalization;
using log4net;
using log4net.Repository.Hierarchy;
using log4net.Appender;
using log4net.Config;
using System.IO;
using System.Reflection;

namespace MetaCollection.Framework.Provider
{
	public class EventProvider : IProvider
	{

		//TODO: Need to implement with log4net.  setup config for debug level per event.
		string m_Description = "Provides Event logging Functionality.";
		Guid m_Id = new Guid("{454C1903-9E23-4e82-BBB0-0F6E97E74960}");
		string m_Name = "EventProvider";
		static readonly ILog Logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);

		//This will come from the config file.
		static string log4NetConfig = @"<log4net debug=""false"">
  <appender name=""FileAppender"" type=""log4net.Appender.RollingFileAppender"">
    <param name=""File"" value=""..\logs\log.txt"" />
    <param name=""AppendToFile"" value=""true"" />
	<rollingStyle value=""Size"" />
	<maxSizeRollBackups value=""10"" />
	<maximumFileSize value=""100KB"" />
<staticLogFileName value=""true"" />
    <layout type=""log4net.Layout.PatternLayout"">
      <param name=""ConversionPattern"" value=""%date [%thread] %-5level %logger [%property{NDC}] - %message%newline"" />
    </layout>
  </appender> 
  <root>
    <appender-ref ref=""FileAppender""/>
    <level value=""ALL""/>""
  </root>
</log4net>";

		/// <summary>
		/// Handles the PropertyChanged event of the context control.
		/// </summary>
		/// <param name="sender">The source of the event.</param>
		/// <param name="e">The <see cref="System.ComponentModel.PropertyChangedEventArgs"/> instance containing the event data.</param>
		void context_PropertyChanged(object sender, PropertyChangedEventArgs e)
		{
			Debug.WriteLine(string.Format(CultureInfo.CurrentCulture, "EventProvider:ItemContext {0} Updated.", new object[] { e.PropertyName }));
			if (e.PropertyName.Equals("ExecutionException", StringComparison.OrdinalIgnoreCase))
			{
				ItemContext c = sender as ItemContext;
				if (c != null)
				{
					Logger.Error(string.Format(CultureInfo.CurrentCulture, "An Exception Occured During the Processing Of the Request. {0}", new object[] { c.ExecutionException.ToString() }));
				}
				else
				{
					Logger.Error("An Error Occured During the Processing of the Request but the Details Could not Be Retrieved.");
				}
			}
		}

		/// <summary>
		/// Extracts the context.
		/// </summary>
		/// <param name="sender">The sender.</param>
		/// <param name="args">The <see cref="System.EventArgs"/> instance containing the event data.</param>
		/// <returns></returns>
		ItemContext ExtractContext(object sender, EventArgs args)
		{
			ItemContext context = sender as ItemContext;
			if (context == null)
			{
				throw new InvalidOperationException("The Current Request Context of this request can not be null.");
			}
			return context;
		}

		/// <summary>
		/// Logs the event.
		/// </summary>
		/// <param name="source">The source.</param>
		/// <param name="sender">The sender.</param>
		/// <param name="args">The <see cref="System.EventArgs"/> instance containing the event data.</param>
		void LogEvent(string source, object sender, EventArgs args)
		{
			ItemContext context = this.ExtractContext(sender, args);
			Logger.Debug(string.Format(CultureInfo.CurrentCulture, "Event: {0} RequestType: {1}  Path: {2} User: {3} PostedItem: {4}  RequestedItem: {5}", source, context.RequestType.ToString(), context.RequestPath.ToString(), context.User.Identity.Name, (context.PostedItem == null) ? "" : context.PostedItem.Id.ToString(), (context.RequestedItem == null) ? "" : context.RequestedItem.Id.ToString()));
		}

		/// <summary>
		/// Configures the log.
		/// </summary>
		static void ConfigureLog()
		{
			byte[] byteArray = Encoding.ASCII.GetBytes(log4NetConfig);
			using (MemoryStream stream = new MemoryStream(byteArray))
			{
				XmlConfigurator.Configure(stream);
			}
		}

		#region IProvider Members

		/// <summary>
		/// Returns a collection of ProviderType records that describe types which this IProvider
		/// depends on or requires for proper operation.  If a provider does not have any dependencies
		/// then an empty set should be returned.
		/// </summary>
		/// <value></value>
		public static IEnumerable<IProviderType> DependantProviders { get { return new List<IProviderType>(); } }

		/// <summary>
		/// Gets the friendly description of this provider.
		/// </summary>
		/// <value>The description.</value>
		public string Description { get { return this.m_Description; } }

		/// <summary>
		/// Unique Identifier of this provider.
		/// </summary>
		/// <value></value>
		public Guid Id { get { return this.m_Id; } }

		/// <summary>
		/// Gets the name of this provider.
		/// </summary>
		/// <value>The name.</value>
		public string Name { get { return this.m_Name; } }

		/// <summary>
		/// Gets the Request Events that are supported by this provider.
		/// </summary>
		/// <value>RequestEventTypes supported by this provider</value>
		public RequestEventTypes SupportedEvents { get { return RequestEventTypes.AllEvents; } }

		/// <summary>
		/// Initializes the provider specified in the ProviderType.
		/// </summary>
		/// <param name="providerConfig">The provider config.</param>
		/// <exception cref="System.ArgumentNullException">The name of the provider is null or one of the Repositories is null and it can not be created internally.</exception>
		/// <exception cref="System.ArgumentException">The name of the provider has a length of zero</exception>
		/// <exception cref="System.InvalidOperationException"> An attempt is made to call
		/// MetaCollection.Framework.Provider.IProvider.Initialize(MetaCollection.Framework.Model.IProviderType)
		/// on a provider after the provider has already been initialized</exception>
		public void Initialize(ProviderReferenceElement providerConfig)
		{
			if (providerConfig == null)
			{
				throw new ArgumentNullException("providerConfig");
			}
			if (string.IsNullOrEmpty(providerConfig.Name))
			{
				throw new ArgumentException("Name of Provider can not be null", "providerConfig");
			}
			m_Name = providerConfig.Name;
			ConfigureLog();			
		}

		#endregion

		#region IRequestSink Members

		public void OnBeforeInvokeRequest(object sender, EventArgs args)
		{
			LogEvent("OnBeforeInvokeRequest", sender, args);
		}

		public void OnBeforePathResolve(object sender, EventArgs args)
		{
			LogEvent("OnBeforePathResolve", sender, args);
		}

		public void OnBeforeValidateRequest(object sender, EventArgs args)
		{
			LogEvent("OnBeforeValidateRequest", sender, args);
		}

		public void OnHandlerDisposing(object sender, EventArgs args)
		{
			LogEvent("OnHandlerDisposing", sender, args);
		}

		public void OnPathResolve(object sender, EventArgs args)
		{
			LogEvent("OnPathResolve", sender, args);
		}

		public void OnPathResolveComplete(object sender, EventArgs args)
		{
			LogEvent("OnPathResolveComplete", sender, args);
		}

		public void OnProvideItem(object sender, EventArgs args)
		{
			this.LogEvent("OnProvideItem", sender, args);
		}

		public void OnProvideResource(object sender, EventArgs args)
		{
			LogEvent("OnProvideResource", sender, args);
		}

		public void OnRequestComplete(object sender, EventArgs args)
		{
			LogEvent("OnRequestComplete", sender, args);
			UnRegisterListener(sender, args);
		}

		public void OnRequestStart(object sender, EventArgs args)
		{
			RegisterListener(sender, args);
			LogEvent("OnRequestStart", sender, args);
		}

		public void OnRequestValidateComplete(object sender, EventArgs args)
		{
			LogEvent("OnRequestValidateComplete", sender, args);
		}

		public void OnValidateRequest(object sender, EventArgs args)
		{
			LogEvent("OnValidateRequest", sender, args);
		}

		private void RegisterListener(object sender, EventArgs args)
		{
			ExtractContext(sender, args).PropertyChanged += new PropertyChangedEventHandler(this.context_PropertyChanged);
		}

		private void UnRegisterListener(object sender, EventArgs args)
		{
			ExtractContext(sender, args).PropertyChanged -= new PropertyChangedEventHandler(this.context_PropertyChanged);
		}


		#endregion
	}
}
